/************************************************************************************
**  
* @copyright (c) 2015-, Technology Co., LTD. All Right Reserved.
*
*************************************************************************************/
/**
* @file     duye_cap_http_service.h
* @version     
* @brief      
* @author   
* @date     2013-01-14
* @note 
*
*  1. 2018-01-14 created this file
* 
*/
#pragma once

#include <string>
#include <map>
#ifndef USE_HASH
//#include <map>
#else
#error("macro \"USE_HASH\" not process")
#endif
#include <duye_cap_filter_engine.h>
#include <duye_cap_packet.h>
#include <duye_cap_service_base.h>
#include <duye_cap_tcp_service.h>
#include <duye_cap_http_session.h>
#include <duye_cap_http_analyser.h>

namespace duye {

class HttpOutput {
public:
	typedef std::pair<std::string /* headerField */, std::string /* headerValue */> HeaderPair;
	typedef std::list<HeaderPair> HeaderPairList;
	typedef struct tag_Body {
		uint8* data;
		int32 len;
	} Body;

public:
	HttpOutput() {}
	HttpOutput(const HttpOutput& httpOutput) {
		*this = httpOutput;
	}
	~HttpOutput() {
		if (m_body.data) {
			free(m_body.data);
		}
	}

	HttpOutput& operator=(const HttpOutput& httpOutput) {
		this->m_srcMac = httpOutput.m_srcMac;
		this->m_dstMac = httpOutput.m_dstMac;
		this->m_srcIpPort.first = httpOutput.m_srcIpPort.first;
		this->m_srcIpPort.second = httpOutput.m_srcIpPort.second;
		this->m_dstIpPort.first = httpOutput.m_dstIpPort.first;
		this->m_dstIpPort.second = httpOutput.m_dstIpPort.second;
		this->m_url = httpOutput.m_url;
		this->m_status = httpOutput.m_status;
		if (httpOutput.m_body.data && httpOutput.m_body.len) {
			if (this->m_body.data) {
				free(this->m_body.data);
			}

			this->m_body.data = (uint8* )malloc(httpOutput.m_body.len);
			memcpy(this->m_body.data, httpOutput.m_body.data, httpOutput.m_body.len);
			this->m_body.len = httpOutput.m_body.len;
		}

		HeaderPairList::const_iterator iter = httpOutput.m_headerPairList.begin();
		for (; iter != httpOutput.m_headerPairList.end(); iter++) {
			this->m_headerPairList.push_back(*iter);
		}

		return *this;
	}

public:
	std::string m_srcMac;
	std::string m_dstMac;
	IPPortPair m_srcIpPort;
	IPPortPair m_dstIpPort;
	std::string m_url;
	std::string m_status;
	Body m_body;
	HeaderPairList m_headerPairList;
};

typedef SmartPtr<HttpOutput> HttpOutputReference;

/**
 * @brief http listener interface
 */
class HttpListener : public DataServiceListener {
public:
	virtual ~HttpListener() {}
	virtual int32 onHttpPkt(const HttpOutputReference& pkt) = 0;
    virtual int32 onHttpSession(const HttpOutputReference& pkt, const HttpSession& httpSession) = 0;
};

typedef std::map<HttpListener*, FilterRule*> HttpListenerMap;

/**
 * @brief http analyser key
 */
class HttpAnalyserKey {
	friend bool operator==(const HttpAnalyserKey& lkey, const HttpAnalyserKey& rkey);
	friend bool operator!=(const HttpAnalyserKey& lkey, const HttpAnalyserKey& rkey);
#ifndef USE_HASH
	friend bool operator<(const HttpAnalyserKey& lkey, const HttpAnalyserKey& rkey);
#else
#error("macro \"USE_HASH\" not process")
#endif

public:
	HttpAnalyserKey() {}

	HttpAnalyserKey(
		uint32 srcIp, 
		uint32 dstIp, 
		uint16 srcPort, 
		uint16 dstPort)
		: m_src(srcIp, srcPort) 
		, m_dst(dstIp, dstPort) {}
	~HttpAnalyserKey() {}

	HttpAnalyserKey& operator=(const HttpAnalyserKey& key) {
		this->m_src.first = key.m_src.first;
		this->m_src.second = key.m_src.second;
		this->m_dst.first = key.m_dst.first;
		this->m_dst.second = key.m_dst.second;
		return *this;
	}

	void SetSrcIp(uint32 ip) {
		m_src.first = ip;
	}

	void SetDstIp(uint32 ip) {
		m_dst.first = ip;
	}

	void SetSrcPort(uint16 port) {
		m_src.second = port;
	}

	void SetDstPort(uint16 port) {
		m_dst.second = port;
	}

private:
	IPPortPair m_src;
	IPPortPair m_dst;
};

inline bool operator==(const HttpAnalyserKey& lkey, const HttpAnalyserKey& rkey) {
	return lkey.m_src.first == rkey.m_src.first && 
		lkey.m_src.second == rkey.m_src.second && 
		lkey.m_dst.first == rkey.m_dst.first && 
		lkey.m_dst.second == rkey.m_dst.second;
}

inline bool operator!=(const HttpAnalyserKey& lkey, const HttpAnalyserKey& rkey) {
	return lkey.m_src.first != rkey.m_src.first ||
		lkey.m_src.second != rkey.m_src.second || 
		lkey.m_dst.first != rkey.m_dst.first || 
		lkey.m_dst.second != rkey.m_dst.second;
}

typedef std::pair<HttpAnalyser*, HttpOutput> HttpAnalyserPair;

#ifndef USE_HASH
inline bool operator<(const HttpAnalyserKey& lkey, const HttpAnalyserKey& rkey) {
	if (lkey.m_src.first < rkey.m_src.first) {
		return true;
	} else if (lkey.m_src.first == rkey.m_src.first && 
		lkey.m_src.second < rkey.m_src.second) {
		return true;
	} else if (lkey.m_src.first == rkey.m_src.first && 
		lkey.m_src.second == rkey.m_src.second && 
		lkey.m_dst.first < rkey.m_dst.first) {
		return true;
	} else if (lkey.m_src.first == rkey.m_src.first && 
		lkey.m_src.second == rkey.m_src.second && 
		lkey.m_dst.first == rkey.m_dst.first && 
		lkey.m_dst.second < rkey.m_dst.second) {
		return true;
	}

	return false;
}

typedef std::map<HttpAnalyserKey, HttpAnalyserPair /* TODO: */> HttpAnalyserMap;
#else
#error("macro \"USE_HASH\" not process")
#endif

/**
 * @brief net layer server interface
 */
class HttpService : public CapDataService, public TcpListener, public Singleton<HttpService> {
public:
	HttpService();
	~HttpService();

	// implementation WdpiService interface 
	virtual bool start();
    // implementation WdpiService interface
	virtual bool stop();
	// implementation WdpiService interface
	virtual bool run();		
	// implementation CapDataService interface
    virtual bool addListener(DataServiceListener* listener, const FilterRule* filterRule = NULL);
	// implementation CapDataService interface
    virtual bool delListener(DataServiceListener* listener);

	// implementation TcpListener interface
	virtual int32 onTcpPacket(const PacketReference& pktRef);
	// implementation TcpListener interface
	virtual int32 onTcpSession(const PacketReference& pktRef, const TcpSession& tcpSession);

private:
	HttpAnalyser* createAnalyser();
	void recycleAnalyser(HttpAnalyser* analyser);
    
private:
    HttpListenerMap m_listenerMap;
	HttpAnalyser m_analyserForSinglePacket;
	HttpAnalyserMap m_analyserForSession;
};

}
